/**
 * axios请求封装
 */
import axios from 'axios';
import type { AxiosInstance } from 'axios';
import qs from 'qs';
import type { requestInterceptors, requestConfig } from './types';

const DEAFULT_LOADING = true;

function regsoleKey(config) {
  const { method, url, params, data } = config;
  return [method, url, qs.stringify(params), qs.stringify(data)].join('&');
}

const reqQueue = new Map();
function addreqQueue(config) {
  // 调用生成唯一标识值函数, 生成 requestKey
  const requestKey = regsoleKey(config);
  // 为每个请求创建一个专属的 CancelToken(用于取消请求)
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      // 判断 reqQueue 中是否含有 requestKey,
      // 将 requestKey 与 CancelToken 以键值对的形式保存到map对象中
      if (!reqQueue.has(requestKey)) {
        reqQueue.set(requestKey, cancel);
      }
    });
}
function generateReqKey(config) {
  return config?.method + config?.url;
}

function removereqQueue(config) {
  // 标识值
  const requestKey = generateReqKey(config);

  if (reqQueue.has(requestKey)) {
    // 取消之前发出请求
    const cancelToken = reqQueue.get(requestKey);
    cancelToken(requestKey);
    // 从队列移除
    reqQueue.delete(requestKey);
  }
}

class request {
  instance: AxiosInstance;

  interceptors?: requestInterceptors;

  showLoading: boolean;

  requestMap: any;

  CancelToken: any;

  constructor(config: requestConfig) {
    // 创建axios实例
    this.instance = axios.create(config);
    this.requestMap = new Map();
    this.CancelToken = axios.CancelToken;

    // 保存基本信息
    this.interceptors = config.interceptors;
    this.showLoading = config.showLoading ?? DEAFULT_LOADING;

    // 使用拦截器
    // 1.从config中取出的拦截器是对应的实例的拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch,
    );
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch,
    );

    // 2.添加所有的实例都有的拦截器
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        removereqQueue(config); // 检查是否重复发送请求
        addreqQueue(config); // 将本次请求加入请求队列
        return config;
      },
      (err) => err,
    );
    // 响应拦截器
    this.instance.interceptors.response.use(
      (response) => {
        removereqQueue(response?.config); // 请求从请求队列移除
        return response;
      },
      (error) => error,
    );
  }

  request<T = any>(config: requestConfig<any>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 1.单个请求对请求config的处理
      if (config.interceptors?.requestInterceptor) {
        config = config.interceptors.requestInterceptor(config);
      }

      // 2.判断每个请求单独是否需要显示loading
      if (config.showLoading === false) {
        this.showLoading = config.showLoading;
      }

      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 1.单个请求对数据的处理
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res);
          }
          // 2.将showLoading设置true, 这样不会影响下一个请求
          this.showLoading = DEAFULT_LOADING;

          // 3.将结果resolve返回出去
          resolve(res);
        })
        .catch((err) => {
          if (config.interceptors?.responseInterceptorCatch) {
            err = config.interceptors.responseInterceptorCatch(err);
          }
          // 将showLoading设置true, 这样不会影响下一个请求
          this.showLoading = DEAFULT_LOADING;

          reject(err);
        });
    });
  }

  get<T = any>(config: requestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'GET' });
  }

  post<T = any>(config: requestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'POST' });
  }

  delete<T = any>(config: requestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'DELETE' });
  }

  patch<T = any>(config: requestConfig<T>): Promise<T> {
    return this.request<T>({ ...config, method: 'PATCH' });
  }
}

export default request;
